<article id="wikiArticle">
<div> <div class="prevnext" style="text-align: right;">
<p><a href="Guide/Indexed_Collections" style="float: left;">« 上一页</a><a href="Guide/Working_with_Objects">下一页  »</a></p>
</div></div>
<p class="summary">这一章介绍由key值标记的数据容器；Map 和 Set 对象承载的数据元素可以按照插入时的顺序被迭代遍历。</p>
<h2 id="映射">映射</h2>
<h3 id="Map对象"><code>Map</code>对象</h3>
<p>ECMAScript 2015 引入了一个新的数据结构来将一个值映射到另一个值。一个<a href="Reference/Map" title="此页面仍未被本地化, 期待您的翻译!"><code>Map</code></a>对象就是一个简单的键值对映射集合，可以按照数据插入时的顺序遍历所有的元素。</p>
<p>下面的代码演示了使用<code>Map</code>进行的一些基本操作。请参考<a href="Reference/Map" title="此页面仍未被本地化, 期待您的翻译!"><code>Map</code></a>以获取更多的样例和完整的 API。你可以使用<a href="Reference/Statements/for...of" title="for...of语句在可迭代对象（包括 Array，Map，Set，String，TypedArray，arguments 对象等等）上创建一个迭代循环，调用自定义迭代钩子，并为每个不同属性的值执行语句"><code>for...of</code></a>循环来得到所有的<code>[key, value]</code>。</p>
<pre><code  class="language-javascript">var sayings = new Map();
sayings.set('dog', 'woof');
sayings.set('cat', 'meow');
sayings.set('elephant', 'toot');
sayings.size; // 3
sayings.get('fox'); // undefined
sayings.has('bird'); // false
sayings.delete('dog');
sayings.has('dog'); // false

for (var [key, value] of sayings) {
  console.log(key + ' goes ' + value);
}
// "cat goes meow"
// "elephant goes toot"

sayings.clear();
sayings.size; // 0</code></pre>
<h3 id="Object和Map的比较"><code><font face="Consolas, Liberation Mono, Courier, monospace">Object</font></code>和<code>Map</code>的比较</h3>
<p>一般地，<a href="Reference/Global_Objects/Object" title="Object 构造函数创建一个对象包装器。">objects</a>会被用于将字符串类型映射到数值。<code>Object</code>允许设置键值对、根据键获取值、删除键、检测某个键是否存在。而<code>Map</code>具有更多的优势。</p>
<ul>
<li><code>Object</code>的键均为<code>Strings</code>类型，在<code>Map</code>里键可以是任意类型。</li>
<li>必须手动计算<code>Object</code>的尺寸，但是可以很容易地获取使用<code>Map</code>的尺寸。</li>
<li><code>Map</code>的遍历遵循元素的插入顺序。</li>
<li><code>Object</code>有原型，所以映射中有一些缺省的键。<font face="Open Sans, Arial, sans-serif">（</font>可以用 <code>map = Object.create(null) 回避</code>）。</li>
</ul>
<p>这三条提示可以帮你决定用<code>Map</code>还是<code>Object</code>：</p>
<ul>
<li>如果键在运行时才能知道，或者所有的键类型相同，所有的值类型相同，那就使用<code>Map</code>。</li>
<li>如果需要将原始值存储为键，则使用<code>Map</code>，因为<code>Object</code>将每个键视为字符串，不管它是一个数字值、布尔值还是任何其他原始值。</li>
<li>如果需要对个别元素进行操作，使用<code>Object</code>。</li>
</ul>
<h3 id="WeakMap对象"><code>WeakMap</code>对象</h3>
<p><a href="Reference/WeakMap" title="此页面仍未被本地化, 期待您的翻译!"><code>WeakMap</code></a>对象也是键值对的集合。它的<strong>键必须是对象类型</strong>，值可以是任意类型。它的键被弱保持，也就是说，当其键所指对象没有其他地方引用的时候，它会被GC回收掉。<code>WeakMap</code>提供的接口与<code>Map</code>相同。</p>
<p>与<code>Map</code>对象不同的是，<code>WeakMap</code>的键是不可枚举的。不提供列出其键的方法。列表是否存在取决于垃圾回收器的状态，是不可预知的。</p>
<p>可以在"Why <em>Weak</em>Map?"<a href="Reference/WeakMap" title="此页面仍未被本地化, 期待您的翻译!"><code>WeakMap</code></a>查看更多信息和示例。</p>
<p><code>WeakMap</code>对象的一个用例是存储一个对象的私有数据或隐藏实施细节。Nick Fitzgerald 的博文<a class="external" href="http://fitzgeraldnick.com/weblog/53/" rel="noopener">"Hiding Implementation Details with ECMAScript 6 WeakMaps"</a>提供了一个例子。对象内部的私有数据和方法被存储在<code>WeakMap</code>类型的<code>privates</code>变量中。所有暴露出的原型和情况都是公开的，而其他内容都是外界不可访问的，因为模块并未导出<code>privates</code>对象。</p>
<pre><code  class="language-javascript">const privates = new WeakMap();

function Public() {
  const me = {
    // Private data goes here
  };
  privates.set(this, me);
}

Public.prototype.method = function () {
  const me = privates.get(this);
  // Do stuff with private data in `me`...
};

module.exports = Public;
</code></pre>
<h2 id="集合">集合</h2>
<h3 id="Set对象"><code>Set</code>对象</h3>
<p><a href="Reference/Global_Objects/Set" title="Set 对象允许你存储任何类型的唯一值，无论是原始值或者是对象引用。"><code>Set</code></a>对象是一组值的集合，这些值是不重复的，可以按照添加顺序来遍历。</p>
<p>这里演示了<code>Set</code>的基本操作，更多示例和完整API可以参考<a href="Reference/Global_Objects/Set" title="Set 对象允许你存储任何类型的唯一值，无论是原始值或者是对象引用。"><code>Set</code></a>。</p>
<pre><code  class="language-javascript">var mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");

mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2

for (let item of mySet) console.log(item);
// 1
// "some text"
</code></pre>
<h3 id="数组和集合的转换">数组和集合的转换</h3>
<p>可以使用<a href="Reference/Global_Objects/Array/from" title="Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。"><code>Array.from</code></a>或<a href="Reference/Operators/Spread_operator">展开操作符</a>来完成集合到数组的转换。同样，<code>Set</code>的构造器接受数组作为参数，可以完成从<code>Array</code>到<code>Set</code>的转换。需要重申的是，<code>Set</code>对象中的值不重复，所以数组转换为集合时，所有重复值将会被删除。</p>
<pre><code  class="language-javascript">Array.from(mySet);
[...mySet2];

mySet2 = new Set([1,2,3,4]);
</code></pre>
<h3 id="Array和Set的对比"><code>Array</code>和<code>Set</code>的对比</h3>
<p>一般情况下，在JavaScript中使用数组来存储一组元素，而新的集合对象有这些优势：</p>
<ul>
<li>数组中用于判断元素是否存在的<a href="Reference/Global_Objects/Array/indexOf" title="indexOf()方法返回在数组中可以找到一个给定元素的第一个索引，如果不存在，则返回-1。"><code>indexOf</code></a> 函数效率低下。</li>
<li><code>Set</code>对象允许根据值删除元素，而数组中必须使用基于下标的 splice 方法。</li>
<li>数组的<code>indexOf</code>方法无法找到<a href="Reference/Global_Objects/NaN" title="全局属性 NaN 的值表示不是一个数字（Not-A-Number）。"><code>NaN</code></a>值。</li>
<li><code>Set</code>对象存储不重复的值，所以不需要手动处理包含重复值的情况。</li>
</ul>
<h3 id="WeakSet对象"><code>WeakSet</code>对象</h3>
<p><a href="Reference/Global_Objects/WeakSet" title="WeakSet 对象允许你将弱保持对象存储在一个集合中。"><code>WeakSet</code></a>对象是一组对象的集合。<code>WeakSet</code>中的对象不重复且不可枚举<font face="Consolas, Liberation Mono, Courier, monospace">。</font></p>
<p>与<a href="Reference/Global_Objects/Set" title="Set 对象允许你存储任何类型的唯一值，无论是原始值或者是对象引用。"><code>Set</code></a>对象的主要区别有：</p>
<ul>
<li><code>WeakSets</code>中的值必须是对象类型，不可以是别的类型</li>
<li><code>WeakSet</code>的“<em>weak</em>”指的是，对集合中的对象，如果不存在其他引用，那么该对象将可被垃圾回收。于是不存在一个当前可用对象组成的列表，所以<code>WeakSets</code>不可枚举</li>
</ul>
<p><code>WeakSet</code>的用例很有限，比如使用DOM元素作为键来追踪它们而不必担心内存泄漏。</p>
<h2 id="Map的键和Set的值的等值判断"><code>Map</code>的键和<code>Set</code>的值的等值判断</h2>
<p><code>Map</code>的键和<code>Set</code>的值的等值判断都基于<a class="external" href="https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero" rel="noopener">same-value-zero algorithm</a>：</p>
<ul>
<li>判断使用与<code>===</code>相似的规则。</li>
<li><code>-0</code>和<code>+0</code>相等。</li>
<li><a href="Reference/Global_Objects/NaN" title="全局属性 NaN 的值表示不是一个数字（Not-A-Number）。"><code>NaN</code></a>与自身相等（与<code>===</code>有所不同）。</li>
</ul>
<p></p><div class="prevnext" style="text-align: right;">
<p><a href="Guide/Indexed_Collections" style="float: left;">« 上一页</a><a href="Guide/Working_with_Objects">下一页  »</a></p>
</div><p></p>
</article>